﻿using System;
using Inovout.Component;
using Newtonsoft.Json;

namespace Inovout.Serializer
{
    public class ComponentJsonConverter : JsonConverter
    {

        /// <summary>
        /// Reads the JSON representation of the object.
        /// </summary>
        /// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
        /// <param name="objectType">Type of the object.</param>
        /// <param name="existingValue">The existing value of object being read.</param>
        /// <param name="serializer">The calling serializer.</param>
        /// <returns>The object value.</returns>
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
            {
                return null;
            }

            object value = Create(objectType);
            if (value == null)
            {
                throw new JsonSerializationException("No object created.");
            }

            serializer.Populate(reader, value);
            return value;
        }

        /// <summary>
        /// Creates an object which will then be populated by the serializer.
        /// </summary>
        /// <param name="objectType">Type of the object.</param>
        /// <returns>The created object.</returns>
        public virtual object Create(Type objectType)
        {
            return ComponentRegistry.Resolve(objectType);
        }

        /// <summary>
        /// Determines whether this instance can convert the specified object type.
        /// </summary>
        /// <param name="objectType">Type of the object.</param>
        /// <returns>
        /// 	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
        /// </returns>
        public override bool CanConvert(Type objectType)
        {
            return ComponentRegistry.IsRegistered(objectType);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
        public override bool CanWrite
        {
            get
            {
                return false;
            }
        }
    }

//    internal static class CollectionUtils
//    {
//        /// <summary>
//        /// Determines whether the collection is null or empty.
//        /// </summary>
//        /// <param name="collection">The collection.</param>
//        /// <returns>
//        /// 	<c>true</c> if the collection is null or empty; otherwise, <c>false</c>.
//        /// </returns>
//        public static bool IsNullOrEmpty<T>(ICollection<T> collection)
//        {
//            if (collection != null)
//            {
//                return (collection.Count == 0);
//            }
//            return true;
//        }

//        /// <summary>
//        /// Adds the elements of the specified collection to the specified generic IList.
//        /// </summary>
//        /// <param name="initial">The list to add to.</param>
//        /// <param name="collection">The collection of elements to add.</param>
//        public static void AddRange<T>(this IList<T> initial, IEnumerable<T> collection)
//        {
//            if (initial == null)
//            {
//                throw new ArgumentNullException();
//            }

//            if (collection == null)
//            {
//                return;
//            }

//            foreach (T value in collection)
//            {
//                initial.Add(value);
//            }
//        }

//#if (NET20 || NET35 || PORTABLE40)
//        public static void AddRange<T>(this IList<T> initial, IEnumerable collection)
//        {
//            //ValidationUtils.ArgumentNotNull(initial, nameof(initial));

//            // because earlier versions of .NET didn't support covariant generics
//            initial.AddRange(collection.Cast<T>());
//        }
//#endif

//        public static bool IsDictionaryType(Type type)
//        {
//            //ValidationUtils.ArgumentNotNull();

//            if (typeof(IDictionary).IsAssignableFrom(type))
//            {
//                return true;
//            }
//            //if (ReflectionUtils.ImplementsGenericDefinition(type, typeof(IDictionary<,>)))
//            //{
//            return true;
//            //}
//#if !(NET40 || NET35 || NET20 || PORTABLE40)
//            //if (ReflectionUtils.ImplementsGenericDefinition(type, typeof(IReadOnlyDictionary<,>)))
//            //{
//            return true;
//            //}
//#endif

//            return false;
//        }

//        public static ConstructorInfo ResolveEnumerableCollectionConstructor(Type collectionType, Type collectionItemType)
//        {
//            Type genericEnumerable = typeof(IEnumerable<>).MakeGenericType(collectionItemType);
//            ConstructorInfo match = null;

//            foreach (ConstructorInfo constructor in collectionType.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
//            {
//                IList<ParameterInfo> parameters = constructor.GetParameters();

//                if (parameters.Count == 1)
//                {
//                    if (genericEnumerable == parameters[0].ParameterType)
//                    {
//                        // exact match
//                        match = constructor;
//                        break;
//                    }

//                    // incase we can't find an exact match, use first inexact
//                    if (match == null)
//                    {
//                        if (genericEnumerable.IsAssignableFrom(parameters[0].ParameterType))
//                        {
//                            match = constructor;
//                        }
//                    }
//                }
//            }

//            return match;
//        }

//        public static bool AddDistinct<T>(this IList<T> list, T value)
//        {
//            return list.AddDistinct(value, EqualityComparer<T>.Default);
//        }

//        public static bool AddDistinct<T>(this IList<T> list, T value, IEqualityComparer<T> comparer)
//        {
//            if (list.ContainsValue(value, comparer))
//            {
//                return false;
//            }

//            list.Add(value);
//            return true;
//        }

//        // this is here because LINQ Bridge doesn't support Contains with IEqualityComparer<T>
//        public static bool ContainsValue<TSource>(this IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer)
//        {
//            if (comparer == null)
//            {
//                comparer = EqualityComparer<TSource>.Default;
//            }

//            if (source == null)
//            {
//                throw new ArgumentNullException();
//            }

//            foreach (TSource local in source)
//            {
//                if (comparer.Equals(local, value))
//                {
//                    return true;
//                }
//            }

//            return false;
//        }

//        public static bool AddRangeDistinct<T>(this IList<T> list, IEnumerable<T> values, IEqualityComparer<T> comparer)
//        {
//            bool allAdded = true;
//            foreach (T value in values)
//            {
//                if (!list.AddDistinct(value, comparer))
//                {
//                    allAdded = false;
//                }
//            }

//            return allAdded;
//        }

//        public static int IndexOf<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
//        {
//            int index = 0;
//            foreach (T value in collection)
//            {
//                if (predicate(value))
//                {
//                    return index;
//                }

//                index++;
//            }

//            return -1;
//        }

//        public static bool Contains(this IEnumerable list, object value, IEqualityComparer comparer)
//        {
//            foreach (object item in list)
//            {
//                if (comparer.Equals(item, value))
//                {
//                    return true;
//                }
//            }
//            return false;
//        }

//        /// <summary>
//        /// Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer{TSource}.
//        /// </summary>
//        /// <typeparam name="TSource">The type of the elements of source.</typeparam>
//        /// <param name="list">A sequence in which to locate a value.</param>
//        /// <param name="value">The object to locate in the sequence</param>
//        /// <param name="comparer">An equality comparer to compare values.</param>
//        /// <returns>The zero-based index of the first occurrence of value within the entire sequence, if found; otherwise, ?.</returns>
//        public static int IndexOf<TSource>(this IEnumerable<TSource> list, TSource value, IEqualityComparer<TSource> comparer)
//        {
//            int index = 0;
//            foreach (TSource item in list)
//            {
//                if (comparer.Equals(item, value))
//                {
//                    return index;
//                }
//                index++;
//            }
//            return -1;
//        }

//        private static IList<int> GetDimensions(IList values, int dimensionsCount)
//        {
//            IList<int> dimensions = new List<int>();

//            IList currentArray = values;
//            while (true)
//            {
//                dimensions.Add(currentArray.Count);

//                // don't keep calculating dimensions for arrays inside the value array
//                if (dimensions.Count == dimensionsCount)
//                {
//                    break;
//                }

//                if (currentArray.Count == 0)
//                {
//                    break;
//                }

//                object v = currentArray[0];
//                if (v is IList)
//                {
//                    currentArray = (IList)v;
//                }
//                else
//                {
//                    break;
//                }
//            }

//            return dimensions;
//        }

//        private static void CopyFromJaggedToMultidimensionalArray(IList values, Array multidimensionalArray, int[] indices)
//        {
//            int dimension = indices.Length;
//            if (dimension == multidimensionalArray.Rank)
//            {
//                multidimensionalArray.SetValue(JaggedArrayGetValue(values, indices), indices);
//                return;
//            }

//            int dimensionLength = multidimensionalArray.GetLength(dimension);
//            IList list = (IList)JaggedArrayGetValue(values, indices);
//            int currentValuesLength = list.Count;
//            if (currentValuesLength != dimensionLength)
//            {
//                throw new Exception("Cannot deserialize non-cubical array as multidimensional array.");
//            }

//            int[] newIndices = new int[dimension + 1];
//            for (int i = 0; i < dimension; i++)
//            {
//                newIndices[i] = indices[i];
//            }

//            for (int i = 0; i < multidimensionalArray.GetLength(dimension); i++)
//            {
//                newIndices[dimension] = i;
//                CopyFromJaggedToMultidimensionalArray(values, multidimensionalArray, newIndices);
//            }
//        }

//        private static object JaggedArrayGetValue(IList values, int[] indices)
//        {
//            IList currentList = values;
//            for (int i = 0; i < indices.Length; i++)
//            {
//                int index = indices[i];
//                if (i == indices.Length - 1)
//                {
//                    return currentList[index];
//                }
//                else
//                {
//                    currentList = (IList)currentList[index];
//                }
//            }
//            return currentList;
//        }

//        public static Array ToMultidimensionalArray(IList values, Type type, int rank)
//        {
//            IList<int> dimensions = GetDimensions(values, rank);

//            while (dimensions.Count < rank)
//            {
//                dimensions.Add(0);
//            }

//            Array multidimensionalArray = Array.CreateInstance(type, dimensions.ToArray());
//            CopyFromJaggedToMultidimensionalArray(values, multidimensionalArray, new int[0]);

//            return multidimensionalArray;
//        }
//    }

//    internal class JsonSerializerInternalWriter : JsonSerializerInternalBase
//    {



//        public JsonSerializerInternalWriter(JsonSerializer serializer)
//            : base(serializer)
//        {
//        }
//        private Type _rootType;
//        private int _rootLevel;
//        private readonly List<object> _serializeStack = new List<object>();


//        public void Serialize(JsonWriter jsonWriter, object value, Type objectType)
//        {
//            if (jsonWriter == null)
//            {
//                throw new ArgumentNullException();
//            }

//            _rootType = objectType;
//            _rootLevel = _serializeStack.Count + 1;

//            JsonContract contract = GetContractSafe(value);

//            try
//            {
//                if (ShouldWriteReference(value, null, contract, null, null))
//                {
//                    WriteReference(jsonWriter, value);
//                }
//                else
//                {
//                    SerializeValue(jsonWriter, value, contract, null, null, null);
//                }
//            }
//            catch (Exception ex)
//            {
//                throw ex;
//            }
//            finally
//            {
//                // clear root contract to ensure that if level was > 1 then it won't
//                // accidently be used for non root values
//                _rootType = null;
//            }
//        }
//        private bool ShouldWriteReference(object value, JsonProperty property, JsonContract valueContract, JsonContainerContract collectionContract, JsonProperty containerProperty)
//        {
//            if (value == null)
//            {
//                return false;
//            }
//            //if (valueContract.ContractType == JsonContractType.Primitive || valueContract.ContractType == JsonContractType.String)
//            //{
//            //    return false;
//            //}

//            bool? isReference = ResolveIsReference(valueContract, property, collectionContract, containerProperty);

//            if (isReference == null)
//            {
//                if (false)//valueContract.ContractType == JsonContractType.Array)
//                {
//                    isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays);
//                }
//                else
//                {
//                    isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects);
//                }
//            }

//            if (!isReference.GetValueOrDefault())
//            {
//                return false;
//            }

//            return GetReferenceResolver().IsReferenced(this, value);
//        }
//        internal IReferenceResolver GetReferenceResolver()
//        {
//            if (this.Serializer.ReferenceResolver == null)
//            {
//                this.Serializer.ReferenceResolver = null;// new DefaultReferenceResolver();
//            }

//            return this.Serializer.ReferenceResolver;
//        }
//        private void WriteReference(JsonWriter writer, object value)
//        {
//            string reference = string.Empty;// GetReference(writer, value);

//            writer.WriteStartObject();
//            writer.WritePropertyName(string.Empty, false);
//            writer.WriteValue(reference);
//            writer.WriteEndObject();
//        }

//        private bool? ResolveIsReference(JsonContract contract, JsonProperty property, JsonContainerContract collectionContract, JsonProperty containerProperty)
//        {
//            bool? isReference = null;

//            // value could be coming from a dictionary or array and not have a property
//            if (property != null)
//            {
//                isReference = property.IsReference;
//            }

//            if (isReference == null && containerProperty != null)
//            {
//                isReference = containerProperty.ItemIsReference;
//            }

//            if (isReference == null && collectionContract != null)
//            {
//                isReference = collectionContract.ItemIsReference;
//            }

//            if (isReference == null)
//            {
//                isReference = contract.IsReference;
//            }

//            return isReference;
//        }

//        private JsonContract GetContractSafe(object value)
//        {
//            if (value == null)
//            {
//                return null;
//            }

//            return this.Serializer.ContractResolver.ResolveContract(value.GetType());
//        }
//        private bool HasFlag(PreserveReferencesHandling value, PreserveReferencesHandling flag)
//        {
//            return ((value & flag) == flag);
//        }
//        private void SerializeValue(JsonWriter writer, object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
//        {
//            if (value == null)
//            {
//                writer.WriteNull();
//                return;
//            }

//            JsonConverter converter =
//                ((member != null) ? member.Converter : null) ??
//                ((containerProperty != null) ? containerProperty.ItemConverter : null) ??
//                ((containerContract != null) ? containerContract.ItemConverter : null) ??
//                valueContract.Converter ??
//                GetMatchingConverter(this.Serializer.Converters, valueContract.UnderlyingType) ??
//               null;// valueContract.InternalConverter;

//            if (converter != null && converter.CanWrite)
//            {
//                SerializeConvertable(writer, converter, value, valueContract, containerContract, containerProperty);
//                return;
//            }
//            SerializeObject(writer, value, (JsonObjectContract)valueContract, member, containerContract, containerProperty);

//            //            switch (valueContract.ContractType)
//            //            {
//            //                case JsonContractType.Object:
//            //                    SerializeObject(writer, value, (JsonObjectContract)valueContract, member, containerContract, containerProperty);
//            //                    break;
//            //                case JsonContractType.Array:
//            //                    JsonArrayContract arrayContract = (JsonArrayContract)valueContract;
//            //                    if (!arrayContract.IsMultidimensionalArray)
//            //                    {
//            //                        SerializeList(writer, (IEnumerable)value, arrayContract, member, containerContract, containerProperty);
//            //                    }
//            //                    else
//            //                    {
//            //                        SerializeMultidimensionalArray(writer, (Array)value, arrayContract, member, containerContract, containerProperty);
//            //                    }
//            //                    break;
//            //                case JsonContractType.Primitive:
//            //                    SerializePrimitive(writer, value, (JsonPrimitiveContract)valueContract, member, containerContract, containerProperty);
//            //                    break;
//            //                case JsonContractType.String:
//            //                    SerializeString(writer, value, (JsonStringContract)valueContract);
//            //                    break;
//            //                case JsonContractType.Dictionary:
//            //                    JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)valueContract;
//            //                    SerializeDictionary(writer, (value is IDictionary) ? (IDictionary)value : dictionaryContract.CreateWrapper(value), dictionaryContract, member, containerContract, containerProperty);
//            //                    break;
//            //#if !(NET35 || NET20 || PORTABLE40)
//            //                case JsonContractType.Dynamic:
//            //                    SerializeDynamic(writer, (IDynamicMetaObjectProvider)value, (JsonDynamicContract)valueContract, member, containerContract, containerProperty);
//            //                    break;
//            //#endif
//            //#if !(DOTNET || PORTABLE40 || PORTABLE)
//            //                case JsonContractType.Serializable:
//            //                    SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract, member, containerContract, containerProperty);
//            //                    break;
//            //#endif
//            //                case JsonContractType.Linq:
//            //                    ((JToken)value).WriteTo(writer, Serializer.Converters.ToArray());
//            //                    break;
//            //            }
//        }
//        internal static JsonConverter GetMatchingConverter(IList<JsonConverter> converters, Type objectType)
//        {

//            if (converters != null)
//            {
//                for (int i = 0; i < converters.Count; i++)
//                {
//                    JsonConverter converter = converters[i];

//                    if (converter.CanConvert(objectType))
//                    {
//                        return converter;
//                    }
//                }
//            }

//            return null;
//        }
//        private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
//        {
//            OnSerializing(writer, contract, value);

//            _serializeStack.Add(value);

//            WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty);

//            int initialDepth = 1;// writer.Top;

//            for (int index = 0; index < contract.Properties.Count; index++)
//            {
//                JsonProperty property = contract.Properties[index];
//                try
//                {
//                    object memberValue;
//                    JsonContract memberContract;

//                    if (!CalculatePropertyValues(writer, value, contract, member, property, out memberContract, out memberValue))
//                    {
//                        continue;
//                    }

//                    //  property.WritePropertyName(writer);
//                    SerializeValue(writer, memberValue, memberContract, property, contract, member);
//                }
//                catch (Exception ex)
//                {

//                    throw ex;
//                }
//            }

//            if (contract.ExtensionDataGetter != null)
//            {
//                IEnumerable<KeyValuePair<object, object>> extensionData = contract.ExtensionDataGetter(value);
//                if (extensionData != null)
//                {
//                    foreach (KeyValuePair<object, object> e in extensionData)
//                    {
//                        JsonContract keyContract = GetContractSafe(e.Key);
//                        JsonContract valueContract = GetContractSafe(e.Value);

//                        bool escape;
//                        string propertyName = GetPropertyName(writer, e.Key, keyContract, out escape);

//                        if (ShouldWriteReference(e.Value, null, valueContract, contract, member))
//                        {
//                            writer.WritePropertyName(propertyName);
//                            WriteReference(writer, e.Value);
//                        }
//                        else
//                        {
//                            if (!CheckForCircularReference(writer, e.Value, null, valueContract, contract, member))
//                            {
//                                continue;
//                            }

//                            writer.WritePropertyName(propertyName);

//                            SerializeValue(writer, e.Value, valueContract, null, contract, member);
//                        }
//                    }
//                }
//            }

//            writer.WriteEndObject();

//            _serializeStack.RemoveAt(_serializeStack.Count - 1);

//            OnSerialized(writer, contract, value);
//        }
//        private void WriteObjectStart(JsonWriter writer, object value, JsonContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
//        {
//            writer.WriteStartObject();

//            bool isReference = ResolveIsReference(contract, member, collectionContract, containerProperty) ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects);
//            // don't make readonly fields the referenced value because they can't be deserialized to
//            if (isReference && (member == null || member.Writable))
//            {
//                WriteReferenceIdProperty(writer, contract.UnderlyingType, value);
//            }
//            //if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionContract, containerProperty))
//            //{
//            //    WriteTypeProperty(writer, contract.UnderlyingType);
//            //}
//        }

//        private void WriteReferenceIdProperty(JsonWriter writer, Type type, object value)
//        {
//            string reference = string.Empty;//GetReference(writer, value);

//            writer.WritePropertyName(string.Empty, false);
//            writer.WriteValue(reference);
//        }

//        private void SerializeConvertable(JsonWriter writer, JsonConverter converter, object value, JsonContract contract, JsonContainerContract collectionContract, JsonProperty containerProperty)
//        {
//            if (ShouldWriteReference(value, null, contract, collectionContract, containerProperty))
//            {
//                WriteReference(writer, value);
//            }
//            else
//            {
//                if (!CheckForCircularReference(writer, value, null, contract, collectionContract, containerProperty))
//                {
//                    return;
//                }

//                _serializeStack.Add(value);

//                converter.WriteJson(writer, value, GetInternalSerializer());

//                _serializeStack.RemoveAt(_serializeStack.Count - 1);
//            }
//        }
//        private dynamic GetInternalSerializer()
//        {
//            return null;
//            //if (InternalSerializer == null)
//            //{
//            //    InternalSerializer = new JsonSerializerProxy(this);
//            //}

//            //return InternalSerializer;
//        }
//        private string GetPropertyName(JsonWriter writer, object name, JsonContract contract, out bool escape)
//        {
//            escape = false;
//            string propertyName = null;
//            return propertyName;
//            //if (contract.ContractType == JsonContractType.Primitive)
//            //{
//            //    JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract;

//            //    escape = true;
//            //    return Convert.ToString(name, CultureInfo.InvariantCulture);

//            //}
//            //else if (TryConvertToString(name, name.GetType(), out propertyName))
//            //{
//            //    escape = true;
//            //    return propertyName;
//            //}
//            //else
//            //{
//            //    escape = true;
//            //    return name.ToString();
//            //}
//        }

//        private bool CalculatePropertyValues(JsonWriter writer, object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, out JsonContract memberContract, out object memberValue)
//        {
//            //   if (!property.Ignored && property.Readable && ShouldSerialize(writer, property, value) && IsSpecified(writer, property, value))
//            if (!property.Ignored && property.Readable)
//            {
//                //if (property.PropertyContract == null)
//                //{
//                //  property.PropertyContract = Serializer.ContractResolver.ResolveContract(property.PropertyType);
//                //}

//                memberValue = property.ValueProvider.GetValue(value);
//                // memberContract = (property.PropertyContract.IsSealed) ? property.PropertyContract : GetContractSafe(memberValue);
//                memberContract = GetContractSafe(memberValue);

//                //  if (ShouldWriteProperty(memberValue, property))
//                if (true)
//                {
//                    if (ShouldWriteReference(memberValue, property, memberContract, contract, member))
//                    {
//                        writer.WritePropertyName(property.PropertyName);
//                        // property.WritePropertyName(writer);
//                        WriteReference(writer, memberValue);
//                        return false;
//                    }

//                    if (!CheckForCircularReference(writer, memberValue, property, memberContract, contract, member))
//                    {
//                        return false;
//                    }

//                    if (memberValue == null)
//                    {
//                        JsonObjectContract objectContract = contract as JsonObjectContract;
//                        Required resolvedRequired = ((objectContract != null) ? objectContract.ItemRequired : null) ?? Required.Default;
//                        if (resolvedRequired == Required.Always)
//                        {
//                            throw new Exception();
//                            //throw JsonSerializationException.Create(null, writer.ContainerPath, "Cannot write a null value for property '{0}'. Property requires a value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName), null);
//                        }
//                        if (resolvedRequired == Required.DisallowNull)
//                        {
//                            throw new Exception();
//                            // throw JsonSerializationException.Create(null, writer.ContainerPath, "Cannot write a null value for property '{0}'. Property requires a non-null value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName), null);
//                        }
//                    }

//                    return true;
//                }
//            }

//            memberContract = null;
//            memberValue = null;
//            return false;
//        }

//        private bool CheckForCircularReference(JsonWriter writer, object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)
//        {
//            //  if (value == null || contract.ContractType == JsonContractType.Primitive || contract.ContractType == JsonContractType.String)
//            if (value == null)
//            {
//                return true;
//            }

//            ReferenceLoopHandling? referenceLoopHandling = null;

//            if (property != null)
//            {
//                referenceLoopHandling = property.ReferenceLoopHandling;
//            }

//            if (referenceLoopHandling == null && containerProperty != null)
//            {
//                referenceLoopHandling = containerProperty.ItemReferenceLoopHandling;
//            }

//            if (referenceLoopHandling == null && containerContract != null)
//            {
//                referenceLoopHandling = containerContract.ItemReferenceLoopHandling;
//            }

//            bool exists = (Serializer.EqualityComparer != null)
//                ? _serializeStack.Contains(value, Serializer.EqualityComparer)
//                : _serializeStack.Contains(value);

//            if (exists)
//            {
//                string message = "Self referencing loop detected";

//                switch (referenceLoopHandling.GetValueOrDefault(Serializer.ReferenceLoopHandling))
//                {
//                    case ReferenceLoopHandling.Error:
//                        throw new Exception();
//                    case ReferenceLoopHandling.Ignore:
//                        if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
//                        {
//                            TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Skipping serializing self referenced value."), null);
//                        }

//                        return false;
//                    case ReferenceLoopHandling.Serialize:
//                        if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
//                        {
//                            TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Serializing self referenced value."), null);
//                        }

//                        return true;
//                }
//            }

//            return true;
//        }

//        //private void SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
//        //{
//        //    IWrappedCollection wrappedCollection = values as IWrappedCollection;
//        //    object underlyingList = wrappedCollection != null ? wrappedCollection.UnderlyingCollection : values;

//        //    OnSerializing(writer, contract, underlyingList);

//        //    _serializeStack.Add(underlyingList);

//        //    bool hasWrittenMetadataObject = false;// WriteStartArray(writer, underlyingList, contract, member, collectionContract, containerProperty);

//        //    writer.WriteStartArray();

//        //    int initialDepth = 0;// writer.Top;

//        //    int index = 0;
//        //    // note that an error in the IEnumerable won't be caught
//        //    foreach (object value in values)
//        //    {
//        //        try
//        //        {
//        //            JsonContract valueContract = GetContractSafe(value);// contract.FinalItemContract ?? ;

//        //            if (ShouldWriteReference(value, null, valueContract, contract, member))
//        //            {
//        //                WriteReference(writer, value);
//        //            }
//        //            else
//        //            {
//        //                if (CheckForCircularReference(writer, value, null, valueContract, contract, member))
//        //                {
//        //                    SerializeValue(writer, value, valueContract, null, contract, member);
//        //                }
//        //            }
//        //        }
//        //        catch (Exception ex)
//        //        {

//        //            throw ex;
//        //        }
//        //        finally
//        //        {
//        //            index++;
//        //        }
//        //    }

//        //    writer.WriteEndArray();

//        //    if (hasWrittenMetadataObject)
//        //    {
//        //        writer.WriteEndObject();
//        //    }

//        //    _serializeStack.RemoveAt(_serializeStack.Count - 1);

//        //    OnSerialized(writer, contract, underlyingList);
//        //}
//        private void OnSerializing(JsonWriter writer, JsonContract contract, object value)
//        {
//            //contract.InvokeOnSerializing(value, Serializer.Context);
//        }
//        private void OnSerialized(JsonWriter writer, JsonContract contract, object value)
//        {
//            //contract.InvokeOnSerialized(value, Serializer.Context);
//        }
//    }
//    internal abstract class JsonSerializerInternalBase
//    {
//        private class ReferenceEqualsEqualityComparer : IEqualityComparer<object>
//        {
//            bool IEqualityComparer<object>.Equals(object x, object y)
//            {
//                return ReferenceEquals(x, y);
//            }

//            int IEqualityComparer<object>.GetHashCode(object obj)
//            {
//                // put objects in a bucket based on their reference
//                return 1;// RuntimeHelpers.GetHashCode(obj);
//            }
//        }

//        private ErrorContext _currentErrorContext;
//        private BidirectionalDictionary<string, object> _mappings;

//        internal readonly JsonSerializer Serializer;
//        internal readonly ITraceWriter TraceWriter;
//        //protected JsonSerializerProxy InternalSerializer;

//        protected JsonSerializerInternalBase(JsonSerializer serializer)
//        {
//            //ValidationUtils.ArgumentNotNull();

//            Serializer = serializer;
//            TraceWriter = serializer.TraceWriter;
//        }

//        internal BidirectionalDictionary<string, object> DefaultReferenceMappings
//        {
//            get
//            {
//                // override equality comparer for object key dictionary
//                // object will be modified as it deserializes and might have mutable hashcode
//                if (_mappings == null)
//                {
//                    _mappings = new BidirectionalDictionary<string, object>(
//                        EqualityComparer<string>.Default,
//                        new ReferenceEqualsEqualityComparer(),
//                        "A different value already has the Id '{0}'.",
//                        "A different Id has already been assigned for value '{0}'.");
//                }

//                return _mappings;
//            }
//        }

//        private ErrorContext GetErrorContext(object currentObject, object member, string path, Exception error)
//        {
//            if (_currentErrorContext == null)
//            {
//                _currentErrorContext = null;// new ErrorContext(currentObject, member, path, error);
//            }

//            if (_currentErrorContext.Error != error)
//            {
//                throw new InvalidOperationException("Current error context error is different to requested error.");
//            }

//            return _currentErrorContext;
//        }

//        protected void ClearErrorContext()
//        {
//            if (_currentErrorContext == null)
//            {
//                throw new InvalidOperationException("Could not clear error context. Error context is already null.");
//            }

//            _currentErrorContext = null;
//        }

//        protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, IJsonLineInfo lineInfo, string path, Exception ex)
//        {
//            return true;
//            //ErrorContext errorContext = GetErrorContext(currentObject, keyValue, path, ex);

//            //if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Error && !errorContext.Traced)
//            //{
//            //    // only write error once
//            //  //  errorContext.Traced = true;

//            //    // kind of a hack but meh. might clean this up later
//            //    string message = (GetType() == typeof(JsonSerializerInternalWriter)) ? "Error serializing" : "Error deserializing";
//            //    if (contract != null)
//            //    {
//            //        message += " " + contract.UnderlyingType;
//            //    }
//            //    message += ". " + ex.Message;

//            //    // add line information to non-json.net exception message
//            //    if (!(ex is JsonException))
//            //    {
//            //        message = JsonPosition.FormatMessage(lineInfo, path, message);
//            //    }

//            //    TraceWriter.Trace(TraceLevel.Error, message, ex);
//            //}

//            //// attribute method is non-static so don't invoke if no object
//            //if (contract != null && currentObject != null)
//            //{
//            //    contract.InvokeOnError(currentObject, Serializer.Context, errorContext);
//            //}

//            //if (!errorContext.Handled)
//            //{
//            //   // Serializer.OnError(new ErrorEventArgs(currentObject, errorContext));
//            //}

//            //return errorContext.Handled;
//        }
//    }
  
//    internal enum JsonContractType
//    {
//        None = 0,
//        Object = 1,
//        Array = 2,
//        Primitive = 3,
//        String = 4,
//        Dictionary = 5,
//        Dynamic = 6,
//        Serializable = 7,
//        Linq = 8
//    }
//   internal class BidirectionalDictionary<TFirst, TSecond>
//    {
//        private readonly IDictionary<TFirst, TSecond> _firstToSecond;
//        private readonly IDictionary<TSecond, TFirst> _secondToFirst;
//        private readonly string _duplicateFirstErrorMessage;
//        private readonly string _duplicateSecondErrorMessage;

//        public BidirectionalDictionary()
//            : this(EqualityComparer<TFirst>.Default, EqualityComparer<TSecond>.Default)
//        {
//        }

//        public BidirectionalDictionary(IEqualityComparer<TFirst> firstEqualityComparer, IEqualityComparer<TSecond> secondEqualityComparer)
//            : this(
//                firstEqualityComparer,
//                secondEqualityComparer,
//                "Duplicate item already exists for '{0}'.",
//                "Duplicate item already exists for '{0}'.")
//        {
//        }

//        public BidirectionalDictionary(IEqualityComparer<TFirst> firstEqualityComparer, IEqualityComparer<TSecond> secondEqualityComparer,
//            string duplicateFirstErrorMessage, string duplicateSecondErrorMessage)
//        {
//            _firstToSecond = new Dictionary<TFirst, TSecond>(firstEqualityComparer);
//            _secondToFirst = new Dictionary<TSecond, TFirst>(secondEqualityComparer);
//            _duplicateFirstErrorMessage = duplicateFirstErrorMessage;
//            _duplicateSecondErrorMessage = duplicateSecondErrorMessage;
//        }

//        public void Set(TFirst first, TSecond second)
//        {
//            TFirst existingFirst;
//            TSecond existingSecond;

//            if (_firstToSecond.TryGetValue(first, out existingSecond))
//            {
//                if (!existingSecond.Equals(second))
//                {
//                    throw new ArgumentException();
//                }
//            }

//            if (_secondToFirst.TryGetValue(second, out existingFirst))
//            {
//                if (!existingFirst.Equals(first))
//                {
//                    throw new ArgumentException();
//                }
//            }

//            _firstToSecond.Add(first, second);
//            _secondToFirst.Add(second, first);
//        }

//        public bool TryGetByFirst(TFirst first, out TSecond second)
//        {
//            return _firstToSecond.TryGetValue(first, out second);
//        }

//        public bool TryGetBySecond(TSecond second, out TFirst first)
//        {
//            return _secondToFirst.TryGetValue(second, out first);
//        }
//    }

//    internal enum JsonContainerType
//    {
//        None = 0,
//        Object = 1,
//        Array = 2,
//        Constructor = 3
//    }

//    internal struct JsonPosition
//    {
//        private static readonly char[] SpecialCharacters = { '.', ' ', '[', ']', '(', ')' };

//        internal JsonContainerType Type;
//        internal int Position;
//        internal string PropertyName;
//        internal bool HasIndex;

//        public JsonPosition(JsonContainerType type)
//        {
//            Type = type;
//            HasIndex = TypeHasIndex(type);
//            Position = -1;
//            PropertyName = null;
//        }

//        internal int CalculateLength()
//        {
//            switch (Type)
//            {
//                case JsonContainerType.Object:
//                    return PropertyName.Length + 5;
//                case JsonContainerType.Array:
//                case JsonContainerType.Constructor:
//                    return 1;//MathUtils.IntLength((ulong)Position) + 2;
//                default:
//                    throw new ArgumentOutOfRangeException("Type");
//            }
//        }

//        internal void WriteTo(StringBuilder sb)
//        {
//            switch (Type)
//            {
//                case JsonContainerType.Object:
//                    string propertyName = PropertyName;
//                    if (propertyName.IndexOfAny(SpecialCharacters) != -1)
//                    {
//                        sb.Append(@"['");
//                        sb.Append(propertyName);
//                        sb.Append(@"']");
//                    }
//                    else
//                    {
//                        if (sb.Length > 0)
//                        {
//                            sb.Append('.');
//                        }

//                        sb.Append(propertyName);
//                    }
//                    break;
//                case JsonContainerType.Array:
//                case JsonContainerType.Constructor:
//                    sb.Append('[');
//                    sb.Append(Position);
//                    sb.Append(']');
//                    break;
//            }
//        }

//        internal static bool TypeHasIndex(JsonContainerType type)
//        {
//            return (type == JsonContainerType.Array || type == JsonContainerType.Constructor);
//        }

//        internal static string BuildPath(List<JsonPosition> positions, JsonPosition? currentPosition)
//        {
//            int capacity = 0;
//            if (positions != null)
//            {
//                for (int i = 0; i < positions.Count; i++)
//                {
//                    capacity += positions[i].CalculateLength();
//                }
//            }
//            if (currentPosition != null)
//            {
//                capacity += currentPosition.GetValueOrDefault().CalculateLength();
//            }

//            StringBuilder sb = new StringBuilder(capacity);
//            if (positions != null)
//            {
//                foreach (JsonPosition state in positions)
//                {
//                    state.WriteTo(sb);
//                }
//            }
//            if (currentPosition != null)
//            {
//                currentPosition.GetValueOrDefault().WriteTo(sb);
//            }

//            return sb.ToString();
//        }

//        internal static string FormatMessage(IJsonLineInfo lineInfo, string path, string message)
//        {
//            // don't add a fullstop and space when message ends with a new line
//            if (!message.EndsWith(Environment.NewLine, StringComparison.Ordinal))
//            {
//                message = message.Trim();

//                //if (!message.EndsWith('.'))
//                //{
//                //    message += ".";
//                //}

//                message += " ";
//            }

//            //  message += "Path '{0}'".FormatWith(CultureInfo.InvariantCulture, path);

//            if (lineInfo != null && lineInfo.HasLineInfo())
//            {
//                //  message += ", line {0}, position {1}".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition);
//            }

//            message += ".";

//            return message;
//        }
//    }

}

